/**
 * Copyright(c) 2013 Jade Techonologies Co., Ltd.
 *
 * History:
 *   13-4-14 下午6:46 Created by dwzhang
 */
package com.jade.framework.base.util.encryption;

import java.lang.ref.SoftReference;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;

/**
 * 3DES + Base64.
 *
 * @author dwzhang
 * @version 1.0 13-4-14 下午6:46
 */
public class TripleDESEncryption implements Encryption, Decryption
{
    private static Key key;

    static {
        try {
            byte[] KEY = Hex.decodeHex("417A4CF807505CF9D36E2538D650236FBB510F7AD437F085".toCharArray());
            key = new SecretKeySpec(KEY, "DESede");
        }
        catch (Throwable t) {
            //do nothing
        }
    }

    /**
     * 加密
     *
     * @param password 密码明文
     * @return 加密后的密文
     */
    public String encrypt(String password)
    {
        Cipher cipher = getCipher(true);
        byte[] out;
        try {
            out = cipher.doFinal(password.getBytes());
            return new String(Base64.encodeBase64(out));
        }
        catch (IllegalBlockSizeException e) {
            throw new IllegalStateException("Illegal block size:" + e.getMessage());
        }
        catch (BadPaddingException e) {
            throw new IllegalStateException("Bad padding:" + e.getMessage());
        }
    }

    private ThreadLocal cipherLocal = new ThreadLocal();

    private Cipher getCipher(boolean encrypt)
    {
        SoftReference ref = (SoftReference)cipherLocal.get();
        Cipher cipher = null;
        if (ref == null || (cipher = (Cipher)ref.get()) == null) {
            try {
                cipher = Cipher.getInstance("DESede");
                cipherLocal.set(new SoftReference(cipher));
            }
            catch (NoSuchAlgorithmException e) {
                //Never
                throw new IllegalStateException("No such algorithm:" + e.getMessage());
            }
            catch (NoSuchPaddingException e) {
                //Never
                throw new IllegalStateException("No such padding:" + e.getMessage());
            }
        }
        try {
            if (encrypt) {
                cipher.init(Cipher.ENCRYPT_MODE, key);
            }
            else {
                cipher.init(Cipher.DECRYPT_MODE, key);
            }
        }
        catch (InvalidKeyException e) {
            throw new IllegalStateException("Invalid key:" + e.getMessage());
        }
        return cipher;
    }

    /**
     * 检查密码是否匹配
     *
     * @param password  需要检查的密码明文
     * @param encrypted 密码密文
     */
    public boolean check(String password, String encrypted)
    {
        Cipher cipher = getCipher(true);
        byte[] out;
        try {
            out = cipher.doFinal(password.getBytes());
            byte[] en = Base64.encodeBase64(out);
            return en.length == encrypted.length() && StringUtils.equals(encrypted, new String(en));
        }
        catch (IllegalBlockSizeException e) {
            throw new IllegalStateException("Illegal block size:" + e.getMessage());
        }
        catch (BadPaddingException e) {
            throw new IllegalStateException("Bad padding:" + e.getMessage());
        }
    }

    /**
     * 解密
     *
     * @param encrypted 密文
     * @return 明文
     */
    public String decrypt(String encrypted)
    {
        Cipher cipher = getCipher(false);
        byte[] out;
        try {
            out = cipher.doFinal(Base64.decodeBase64(encrypted));
            return new String(out);
        }
        catch (IllegalBlockSizeException e) {
            throw new IllegalStateException("Illegal block size:" + e.getMessage());
        }
        catch (BadPaddingException e) {
            throw new IllegalStateException("Bad padding:" + e.getMessage());
        }
    }
}

